/*jshint undef: true */

/*global jQuery: true */



/*

   --------------------------------

   Infinite Scroll

   --------------------------------

   + https://github.com/paulirish/infinite-scroll

   + version 2.0b2.120519

   + Copyright 2011/12 Paul Irish & Luke Shumard

   + Licensed under the MIT license



   + Documentation: http://infinite-scroll.com/

*/



(function (window, $, undefined) {

	"use strict";



    $.infinitescroll = function infscr(options, callback, element) {

        this.element = $(element);



        // Flag the object in the event of a failed creation

        if (!this._create(options, callback)) {

            this.failed = true;

        }

    };



    $.infinitescroll.defaults = {

        loading: {

            finished: undefined,

            finishedMsg: "All items loaded",

			img: "",

            msg: null,

            msgText: "",

            selector: null,

            speed: 'fast',

            start: undefined

        },

        state: {

            isDuringAjax: false,

            isInvalidPage: false,

            isDestroyed: false,

            isDone: false, // For when it goes all the way through the archive.

            isPaused: false,

            currPage: 1

        },

        debug: false,

		behavior: undefined,

        binder: $(window), // used to cache the selector

        nextSelector: "div#pagination a:first",

        navSelector: "div#pagination",

        contentSelector: null, // rename to pageFragment

        extraScrollPx: 150,

        itemSelector: ".post",

        animate: false,

        pathParse: undefined,

        dataType: 'html',

        appendCallback: true,

        bufferPx: 40,

        errorCallback: function () { },

        infid: 0, //Instance ID

        pixelsFromNavToBottom: undefined,

        path: undefined, // Either parts of a URL as an array (e.g. ["/page/", "/"] or a function that takes in the page number and returns a URL

		prefill: false, // When the document is smaller than the window, load data until the document is larger or links are exhausted

        maxPage: undefined // to manually control maximum page (when maxPage is undefined, maximum page limitation is not work)

	};



    $.infinitescroll.prototype = {



        /*	

            ----------------------------

            Private methods

            ----------------------------

            */



        // Bind or unbind from scroll

        _binding: function infscr_binding(binding) {



            var instance = this,

            opts = instance.options;



            opts.v = '2.0b2.120520';



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_binding_'+opts.behavior] !== undefined) {

                this['_binding_'+opts.behavior].call(this);

                return;

            }



            if (binding !== 'bind' && binding !== 'unbind') {

                this._debug('Binding value  ' + binding + ' not valid');

                return false;

            }



            if (binding === 'unbind') {

                (this.options.binder).unbind('smartscroll.infscr.' + instance.options.infid);

            } else {

                (this.options.binder)[binding]('smartscroll.infscr.' + instance.options.infid, function () {

                    instance.scroll();

                });

            }



            this._debug('Binding', binding);

        },



        // Fundamental aspects of the plugin are initialized

        _create: function infscr_create(options, callback) {



            // Add custom options to defaults

            var opts = $.extend(true, {}, $.infinitescroll.defaults, options);

			this.options = opts;

			var $window = $(window);

			var instance = this;



			// Validate selectors

            if (!instance._validate(options)) {

				return false;

			}



            // Validate page fragment path

            var path = $(opts.nextSelector).attr('href');

            if (!path) {

                this._debug('Navigation selector not found');

                return false;

            }



            // Set the path to be a relative URL from root.

            opts.path = opts.path || this._determinepath(path);



            // contentSelector is 'page fragment' option for .load() / .ajax() calls

            opts.contentSelector = opts.contentSelector || this.element;



            // loading.selector - if we want to place the load message in a specific selector, defaulted to the contentSelector

            opts.loading.selector = opts.loading.selector || opts.contentSelector;



            var ascend_loader = ($('body').hasClass('ascend')) ? '<span class="default-loading-icon spin"></span>' : '';

            var ascend_loader_class = ($('body').hasClass('ascend')) ? 'default_loader ' : '';



            // Define loading.msg

            opts.loading.msg = opts.loading.msg || $('<div class="infscr-loading-wrap"><div id="infscr-loading" class='+ascend_loader_class+'><img alt="Loading..." src="' + opts.loading.img + '" /> '+ascend_loader+' <div>' + opts.loading.msgText + '</div></div></div>');



            // Preload loading.img

            (new Image()).src = opts.loading.img;



            // distance from nav links to bottom

            // computed as: height of the document + top offset of container - top offset of nav link

            if(opts.pixelsFromNavToBottom === undefined) {

				opts.pixelsFromNavToBottom = $(document).height() - $(opts.navSelector).offset().top;

			}



			var self = this;



            // determine loading.start actions

            opts.loading.start = opts.loading.start || function() {

                $(opts.navSelector).hide();

                opts.loading.msg

                .appendTo('.container-wrap')

                .show(0, $.proxy(function() {

					this.beginAjax(opts);

				}, self)).transition({ scale: 1, 'opacity':1,'height':60,'padding-top':35, 'padding-bottom':35 },400,'easeOutCubic');

            };



            // determine loading.finished actions

            opts.loading.finished = opts.loading.finished || function() {

                opts.loading.msg.stop().transition({ scale: 0.5, 'opacity':0 },400,'easeOutCubic');

            };



			// callback loading

            opts.callback = function(instance, data, url) {

                if (!!opts.behavior && instance['_callback_'+opts.behavior] !== undefined) {

                    instance['_callback_'+opts.behavior].call($(opts.contentSelector)[0], data, url);

                }



                if (callback) {

                    callback.call($(opts.contentSelector)[0], data, opts, url);

                }



				if (opts.prefill) {

					$window.bind("resize.infinite-scroll", instance._prefill);

				}

            };



			if (options.debug) {

				// Tell IE9 to use its built-in console

				if (Function.prototype.bind && (typeof console === 'object' || typeof console === 'function') && typeof console.log === "object") {

					["log","info","warn","error","assert","dir","clear","profile","profileEnd"]

						.forEach(function (method) {

							console[method] = this.call(console[method], console);

						}, Function.prototype.bind);

				}

			}



            this._setup();



			// Setups the prefill method for use

			if (opts.prefill) {

				this._prefill();

			}

			

			

			$('.portfolio-wrap, #post-area.masonry').css('margin-bottom',0);



            // Return true to indicate successful creation

            return true;

        },



		_prefill: function infscr_prefill() {

			var instance = this;

			var $document = $(document);

			var $window = $(window);



			function needsPrefill() {

				return ($document.height() <= $window.height());

			}



			this._prefill = function() {

				if (needsPrefill()) {

					instance.scroll();

				}



				$window.bind("resize.infinite-scroll", function() {

					if (needsPrefill()) {

						$window.unbind("resize.infinite-scroll");

						instance.scroll();

					}

				});

			};



			// Call self after setting up the new function

			this._prefill();

		},



        // Console log wrapper

        _debug: function infscr_debug() {

			if (true !== this.options.debug) {

				return;

			}



			if (typeof console !== 'undefined' && typeof console.log === 'function') {

				// Modern browsers

				// Single argument, which is a string

				if ((Array.prototype.slice.call(arguments)).length === 1 && typeof Array.prototype.slice.call(arguments)[0] === 'string') {

					console.log( (Array.prototype.slice.call(arguments)).toString() );

				} else {

					console.log( Array.prototype.slice.call(arguments) );

				}

			} else if (!Function.prototype.bind && typeof console !== 'undefined' && typeof console.log === 'object') {

				// IE8

				Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));

			}

        },



        // find the number to increment in the path.

        _determinepath: function infscr_determinepath(path) {



            var opts = this.options;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_determinepath_'+opts.behavior] !== undefined) {

                return this['_determinepath_'+opts.behavior].call(this,path);

            }



            if (!!opts.pathParse) {



                this._debug('pathParse manual');

                return opts.pathParse(path, this.options.state.currPage+1);



            } else if (path.match(/^(.*?)\b2\b(.*?$)/)) {

                path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1);



                // if there is any 2 in the url at all.    

            } else if (path.match(/^(.*?)2(.*?$)/)) {



                // page= is used in django:

                // http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127

                if (path.match(/^(.*?page=)2(\/.*|$)/)) {

                    path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1);

                    return path;

                }



                path = path.match(/^(.*?)2(.*?$)/).slice(1);



            } else {



                // page= is used in drupal too but second page is page=1 not page=2:

                // thx Jerod Fritz, vladikoff

                if (path.match(/^(.*?page=)1(\/.*|$)/)) {

                    path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1);

                    return path;

                } else {

                    this._debug('Sorry, we couldn\'t parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.');

                    // Get rid of isInvalidPage to allow permalink to state

                    opts.state.isInvalidPage = true;  //prevent it from running on this page.

                }

            }

            this._debug('determinePath', path);

            return path;



        },



        // Custom error

        _error: function infscr_error(xhr) {



            var opts = this.options;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_error_'+opts.behavior] !== undefined) {

                this['_error_'+opts.behavior].call(this,xhr);

                return;

            }



            if (xhr !== 'destroy' && xhr !== 'end') {

                xhr = 'unknown';

            }



            this._debug('Error', xhr);



            if (xhr === 'end') {

                this._showdonemsg();

            }



            opts.state.isDone = true;

            opts.state.currPage = 1; // if you need to go back to this instance

            opts.state.isPaused = false;

            this._binding('unbind');



        },



        // Load Callback

        _loadcallback: function infscr_loadcallback(box, data, url) {

            var opts = this.options,

            callback = this.options.callback, // GLOBAL OBJECT FOR CALLBACK

            result = (opts.state.isDone) ? 'done' : (!opts.appendCallback) ? 'no-append' : 'append',

            frag;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_loadcallback_'+opts.behavior] !== undefined) {

                this['_loadcallback_'+opts.behavior].call(this,box,data);

                return;

            }



			switch (result) {

				case 'done':

					this._showdonemsg();

					return false;



				case 'no-append':

					if (opts.dataType === 'html') {

						data = '<div>' + data + '</div>';

						data = $(data).find(opts.itemSelector);

					}

					break;



				case 'append':

					var children = box.children();

					// if it didn't return anything

					if (children.length === 0) {

						return this._error('end');

					}



					// use a documentFragment because it works when content is going into a table or UL

					frag = document.createDocumentFragment();

					while (box[0].firstChild) {

						frag.appendChild(box[0].firstChild);

					}



					this._debug('contentSelector', $(opts.contentSelector)[0]);

					$(opts.contentSelector)[0].appendChild(frag);

					// previously, we would pass in the new DOM element as context for the callback

					// however we're now using a documentfragment, which doesn't have parents or children,

					// so the context is the contentContainer guy, and we pass in an array

					// of the elements collected as the first argument.



					data = children.get();

					break;

			}



            // loadingEnd function

            opts.loading.finished.call($(opts.contentSelector)[0],opts);



            // smooth scroll to ease in the new content

            if (opts.animate) {

                var scrollTo = $(window).scrollTop() + $('#infscr-loading').height() + opts.extraScrollPx + 'px';

                $('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.state.isDuringAjax = false; });

            }



            if (!opts.animate) {

				// once the call is done, we can allow it again.

				opts.state.isDuringAjax = false;

			}



            callback(this, data, url);



			if (opts.prefill) {

				this._prefill();

			}

		},



        _nearbottom: function infscr_nearbottom() {



            var opts = this.options,

            pixelsFromWindowBottomToBottom = 0 + $(document).height() - (opts.binder.scrollTop()) - $(window).height();



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_nearbottom_'+opts.behavior] !== undefined) {

                return this['_nearbottom_'+opts.behavior].call(this);

            }



            this._debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom);



            // if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom....

            return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom);



        },



        // Pause / temporarily disable plugin from firing

        _pausing: function infscr_pausing(pause) {



            var opts = this.options;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_pausing_'+opts.behavior] !== undefined) {

                this['_pausing_'+opts.behavior].call(this,pause);

                return;

            }



            // If pause is not 'pause' or 'resume', toggle it's value

            if (pause !== 'pause' && pause !== 'resume' && pause !== null) {

                this._debug('Invalid argument. Toggling pause value instead');

            }



            pause = (pause && (pause === 'pause' || pause === 'resume')) ? pause : 'toggle';



            switch (pause) {

                case 'pause':

                    opts.state.isPaused = true;

                break;



                case 'resume':

                    opts.state.isPaused = false;

                break;



                case 'toggle':

                    opts.state.isPaused = !opts.state.isPaused;

                break;

            }



            this._debug('Paused', opts.state.isPaused);

            return false;



        },



        // Behavior is determined

        // If the behavior option is undefined, it will set to default and bind to scroll

        _setup: function infscr_setup() {



            var opts = this.options;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_setup_'+opts.behavior] !== undefined) {

                this['_setup_'+opts.behavior].call(this);

                return;

            }



            this._binding('bind');



            return false;



        },



        // Show done message

        _showdonemsg: function infscr_showdonemsg() {



            var opts = this.options;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['_showdonemsg_'+opts.behavior] !== undefined) {

                this['_showdonemsg_'+opts.behavior].call(this);

                return;

            }

			

			 

            opts.loading.msg

            .find('img, .default-loading-icon')

            .hide()

            .parent()

            .find('div').html($('#pagination').attr('data-is-text')).animate({ opacity: 1 }, 500, function () {

                $(this).parent().delay(2100).transition({ scale: 0.4, 'opacity':0 },500,'easeOutCubic');

                $(this).parent().parent().delay(2100).animate({'height':'0', 'padding':0},500,'easeOutCubic');

            });

       

            

            opts.loading.msg.find('#infscr-loading').css({'height':'auto', 'width' : 'auto', 'padding' : '13px 17px' ,'background-image' : 'none', 'border-width' : '2px'})



            // user provided callback when done    

            opts.errorCallback.call($(opts.contentSelector)[0],'done');

        },



        // grab each selector option and see if any fail

        _validate: function infscr_validate(opts) {

            for (var key in opts) {

                if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) {

                    this._debug('Your ' + key + ' found no elements.');

                    return false;

                }

            }



            return true;

        },



        /*	

            ----------------------------

            Public methods

            ----------------------------

            */



        // Bind to scroll

        bind: function infscr_bind() {

            this._binding('bind');

        },



        // Destroy current instance of plugin

        destroy: function infscr_destroy() {

            this.options.state.isDestroyed = true;

			this.options.loading.finished();

            return this._error('destroy');

        },



        // Set pause value to false

        pause: function infscr_pause() {

            this._pausing('pause');

        },



        // Set pause value to false

        resume: function infscr_resume() {

            this._pausing('resume');

        },



		beginAjax: function infscr_ajax(opts) {

			var instance = this,

				path = opts.path,

				box, desturl, method, condition;



			// increment the URL bit. e.g. /page/3/

			opts.state.currPage++;



            // Manually control maximum page 

            if ( opts.maxPage != undefined && opts.state.currPage > opts.maxPage ){

                this.destroy();

                return;

            }



			// if we're dealing with a table we can't use DIVs

			box = $(opts.contentSelector).is('table') ? $('<tbody/>') : $('<div/>');



			desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);

			instance._debug('heading into ajax', desturl);



			method = (opts.dataType === 'html' || opts.dataType === 'json' ) ? opts.dataType : 'html+callback';

			if (opts.appendCallback && opts.dataType === 'html') {

				method += '+callback';

			}



			switch (method) {

				case 'html+callback':

					instance._debug('Using HTML via .load() method');

					box.load(desturl + ' ' + opts.itemSelector, undefined, function infscr_ajax_callback(responseText) {

						instance._loadcallback(box, responseText, desturl);

					});



					break;



				case 'html':

					instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');

					$.ajax({

						// params

						url: desturl,

						dataType: opts.dataType,

						complete: function infscr_ajax_callback(jqXHR, textStatus) {

							condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === "success" || textStatus === "notmodified");

							if (condition) {

								instance._loadcallback(box, jqXHR.responseText, desturl);

							} else {

								instance._error('end');

							}

						}

					});



					break;

				case 'json':

					instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');

					$.ajax({

						dataType: 'json',

						type: 'GET',

						url: desturl,

						success: function (data, textStatus, jqXHR) {

							condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === "success" || textStatus === "notmodified");

							if (opts.appendCallback) {

								// if appendCallback is true, you must defined template in options.

								// note that data passed into _loadcallback is already an html (after processed in opts.template(data)).

								if (opts.template !== undefined) {

									var theData = opts.template(data);

									box.append(theData);

									if (condition) {

										instance._loadcallback(box, theData);

									} else {

										instance._error('end');

									}

								} else {

									instance._debug("template must be defined.");

									instance._error('end');

								}

							} else {

								// if appendCallback is false, we will pass in the JSON object. you should handle it yourself in your callback.

								if (condition) {

									instance._loadcallback(box, data, desturl);

								} else {

									instance._error('end');

								}

							}

						},

						error: function() {

							instance._debug("JSON ajax request failed.");

							instance._error('end');

						}

					});



					break;

			}

		},



        // Retrieve next set of content items

        retrieve: function infscr_retrieve(pageNum) {

			pageNum = pageNum || null;



			var instance = this,

            opts = instance.options;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['retrieve_'+opts.behavior] !== undefined) {

                this['retrieve_'+opts.behavior].call(this,pageNum);

                return;

            }



            // for manual triggers, if destroyed, get out of here

            if (opts.state.isDestroyed) {

                this._debug('Instance is destroyed');

                return false;

            }



            // we dont want to fire the ajax multiple times

            opts.state.isDuringAjax = true;



            opts.loading.start.call($(opts.contentSelector)[0],opts);

        },



        // Check to see next page is needed

        scroll: function infscr_scroll() {



            var opts = this.options,

            state = opts.state;



            // if behavior is defined and this function is extended, call that instead of default

            if (!!opts.behavior && this['scroll_'+opts.behavior] !== undefined) {

                this['scroll_'+opts.behavior].call(this);

                return;

            }



            if (state.isDuringAjax || state.isInvalidPage || state.isDone || state.isDestroyed || state.isPaused) {

				return;

			}



            if (!this._nearbottom()) {

				return;

			}



            this.retrieve();



        },



        // Toggle pause value

        toggle: function infscr_toggle() {

            this._pausing();

        },



        // Unbind from scroll

        unbind: function infscr_unbind() {

            this._binding('unbind');

        },



        // update options

        update: function infscr_options(key) {

            if ($.isPlainObject(key)) {

                this.options = $.extend(true,this.options,key);

            }

        }

    };





    /*	

        ----------------------------

        Infinite Scroll function

        ----------------------------



        Borrowed logic from the following...



        jQuery UI

        - https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js



        jCarousel

        - https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js



        Masonry

        - https://github.com/desandro/masonry/blob/master/jquery.masonry.js		



*/



    $.fn.infinitescroll = function infscr_init(options, callback) {





        var thisCall = typeof options;



        switch (thisCall) {



            // method 

            case 'string':

                var args = Array.prototype.slice.call(arguments, 1);



				this.each(function () {

					var instance = $.data(this, 'infinitescroll');



					if (!instance) {

						// not setup yet

						// return $.error('Method ' + options + ' cannot be called until Infinite Scroll is setup');

						return false;

					}



					if (!$.isFunction(instance[options]) || options.charAt(0) === "_") {

						// return $.error('No such method ' + options + ' for Infinite Scroll');

						return false;

					}



					// no errors!

					instance[options].apply(instance, args);

				});



            break;



            // creation 

            case 'object':



                this.each(function () {



                var instance = $.data(this, 'infinitescroll');



                if (instance) {



                    // update options of current instance

                    instance.update(options);



                } else {



                    // initialize new instance

                    instance = new $.infinitescroll(options, callback, this);



                    // don't attach if instantiation failed

                    if (!instance.failed) {

                        $.data(this, 'infinitescroll', instance);

                    }



                }



            });



            break;



        }



        return this;

    };







    /* 

     * smartscroll: debounced scroll event for jQuery *

     * https://github.com/lukeshumard/smartscroll

     * Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js *

     * Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. *

     */



    var event = $.event,

    scrollTimeout;



    event.special.smartscroll = {

        setup: function () {

            $(this).bind("scroll", event.special.smartscroll.handler);

        },

        teardown: function () {

            $(this).unbind("scroll", event.special.smartscroll.handler);

        },

        handler: function (event, execAsap) {

            // Save the context

            var context = this,

            args = arguments;



            // set correct event type

            event.type = "smartscroll";



            if (scrollTimeout) { clearTimeout(scrollTimeout); }

            scrollTimeout = setTimeout(function () {

                $(context).trigger('smartscroll', args);

            }, execAsap === "execAsap" ? 0 : 100);

        }

    };



    $.fn.smartscroll = function (fn) {

        return fn ? this.bind("smartscroll", fn) : this.trigger("smartscroll", ["execAsap"]);

    };





})(window, jQuery);

